From 0ada151b052f53e3ba4471cbb4b818d22038a293 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Fri, 2 Mar 2007 22:57:27 +0000 Subject: [PATCH] x86/64: Save/restore user pagetable pointer for x86/64 PV guests. Signed-off-by: Keir Fraser --- tools/libxc/xc_linux_restore.c | 24 +++++++++++++++++++++++- tools/libxc/xc_linux_save.c | 16 ++++++++++++++-- xen/arch/x86/domain.c | 25 +++++++++++++++++++++++++ xen/arch/x86/domctl.c | 7 +++++++ xen/include/public/arch-x86/xen.h | 1 + 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/tools/libxc/xc_linux_restore.c b/tools/libxc/xc_linux_restore.c index 7d1933c1eb..6fa0388d3f 100644 --- a/tools/libxc/xc_linux_restore.c +++ b/tools/libxc/xc_linux_restore.c @@ -19,7 +19,7 @@ static unsigned long max_mfn; /* virtual starting address of the hypervisor */ static unsigned long hvirt_start; -/* #levels of page tables used by the currrent guest */ +/* #levels of page tables used by the current guest */ static unsigned int pt_levels; /* total number of pages used by the current guest */ @@ -857,6 +857,28 @@ int xc_linux_restore(int xc_handle, int io_fd, ctxt.ctrlreg[3] = xen_pfn_to_cr3(p2m[pfn]); + /* Guest pagetable (x86/64) stored in otherwise-unused CR1. */ + if ( (pt_levels == 4) && ctxt.ctrlreg[1] ) + { + pfn = xen_cr3_to_pfn(ctxt.ctrlreg[1]); + + if (pfn >= max_pfn) { + ERROR("User PT base is bad: pfn=%lu max_pfn=%lu type=%08lx", + pfn, max_pfn, pfn_type[pfn]); + goto out; + } + + if ( (pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) != + ((unsigned long)pt_levels<> L2_PAGETABLE_SHIFT); @@ -1279,6 +1279,18 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, ctxt.ctrlreg[3] = xen_pfn_to_cr3(mfn_to_pfn(xen_cr3_to_pfn(ctxt.ctrlreg[3]))); + /* Guest pagetable (x86/64) stored in otherwise-unused CR1. */ + if ( (pt_levels == 4) && ctxt.ctrlreg[1] ) + { + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(xen_cr3_to_pfn(ctxt.ctrlreg[1])) ) { + ERROR("PT base is not in range of pseudophys map"); + goto out; + } + /* Least-significant bit means 'valid PFN'. */ + ctxt.ctrlreg[1] = 1 | + xen_pfn_to_cr3(mfn_to_pfn(xen_cr3_to_pfn(ctxt.ctrlreg[1]))); + } + if (!write_exact(io_fd, &ctxt, sizeof(ctxt))) { ERROR("Error when writing to state file (1) (errno %d)", errno); goto out; diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 379adca582..57fc717915 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -641,6 +641,31 @@ int arch_set_info_guest( } v->arch.guest_table = pagetable_from_pfn(cr3_pfn); + +#ifdef __x86_64__ + if ( c.nat->ctrlreg[1] ) + { + cr3_pfn = gmfn_to_mfn(d, xen_cr3_to_pfn(c.nat->ctrlreg[1])); + + if ( !mfn_valid(cr3_pfn) || + (paging_mode_refcounts(d) + ? !get_page(mfn_to_page(cr3_pfn), d) + : !get_page_and_type(mfn_to_page(cr3_pfn), d, + PGT_base_page_table)) ) + { + cr3_pfn = pagetable_get_pfn(v->arch.guest_table); + v->arch.guest_table = pagetable_null(); + if ( paging_mode_refcounts(d) ) + put_page(mfn_to_page(cr3_pfn)); + else + put_page_and_type(mfn_to_page(cr3_pfn)); + destroy_gdt(v); + return -EINVAL; + } + + v->arch.guest_table_user = pagetable_from_pfn(cr3_pfn); + } +#endif } #ifdef CONFIG_COMPAT else diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c index c6a81d3a26..d8ca0b0621 100644 --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -470,8 +470,15 @@ void arch_get_info_guest(struct vcpu *v, vcpu_guest_context_u c) c(user_regs.eflags |= v->arch.iopl << 12); if ( !IS_COMPAT(v->domain) ) + { c.nat->ctrlreg[3] = xen_pfn_to_cr3( pagetable_get_pfn(v->arch.guest_table)); +#ifdef __x86_64__ + if ( !pagetable_is_null(v->arch.guest_table_user) ) + c.nat->ctrlreg[1] = xen_pfn_to_cr3( + pagetable_get_pfn(v->arch.guest_table_user)); +#endif + } #ifdef CONFIG_COMPAT else { diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h index 22fcd69a65..f927130058 100644 --- a/xen/include/public/arch-x86/xen.h +++ b/xen/include/public/arch-x86/xen.h @@ -132,6 +132,7 @@ struct vcpu_guest_context { unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ + /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */ unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ #ifdef __i386__ -- 2.30.2